package com.sway.data.pool;

import com.sway.data.core.SwayDataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayDeque;
import java.util.Date;
import java.util.Deque;
import java.util.Iterator;

@ConfigurationProperties(prefix = "spring.sway.data.pool")
public class ConnectionHolder {

    private Deque<ConnectionWithTime> connections;
    private Long timeout = 300L; //unit seconds
    private int capacity = 16;
    private SwayDataSource dataSource;


    public void init(SwayDataSource dataSource) {
        this.connections = new ArrayDeque<>(this.capacity);
        this.dataSource = dataSource;
        for (int i = 0; i < this.capacity; i++) {
            Connection connection = dataSource.build();
            this.push(connection);
        }
    }

    public Long getTimeout() {
        return timeout;
    }

    public void setTimeout(Long timeout) {
        this.timeout = timeout;
    }

    public int getCapacity() {
        return capacity;
    }

    public void setCapacity(int capacity) {
        this.capacity = capacity;
    }

    private class ConnectionWithTime{
        private Connection connection;
        private Long beginTime;

        public ConnectionWithTime(Connection connection){
            this.connection = connection;
            this.beginTime = this.nowTime();
        }

        private Boolean isOverdue(){
            return (this.nowTime() - this.beginTime) > timeout*1000;
        }
        private Long nowTime(){
            return new Date().getTime();
        }

    }

    public synchronized void push(Connection connection){
        clearTimeoutConnection();
        if (this.connections.size()<this.capacity){
            this.connections.addFirst(new ConnectionWithTime(connection));
        }else {
            try {
                connection.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }
    public synchronized Connection pop(){
        if (this.connections.isEmpty()) {
            init(dataSource);
        }
        return this.connections.removeFirst().connection;
    }
    public synchronized void close(Connection connection){
        try {
            connection.close();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }

    private void clearTimeoutConnection(){
        Iterator<ConnectionWithTime> iterator = connections.descendingIterator();
        while (iterator.hasNext()){
            ConnectionWithTime conn = iterator.next();
            if (conn.isOverdue()){
                try {
                    conn.connection.close();
                    iterator.remove();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }else{
                break;
            }
        }
    }

}
